import json
import datetime
from datetime import timedelta
from sqlalchemy.orm import Session
import prometheus_client
from prometheus_client import Counter, Gauge
from prometheus_client.core import CollectorRegistry

from common.welab_doris import WelabDoris
from app.monitor_doris.models.monitor_doris_service import MonitorDorisService
from app.monitor_doris.handlers.monitor_doris_exclude_handler import MonitorDorisExcludeHandler
from app.monitor_doris.handlers.monitor_doris_url_handler import MonitorDorisUrlHandler
from app.monitor_doris.handlers.character_handler import character_log_output
from app.notice_center.handlers.nc_log_alarm_handler import NCLogAlarmHandler
from core.logger import logger
from common.utils import CommonTimeUtils

from setting import config


class MonitorDorisServiceHandler():
    """ Service  Handler
    """
    
    def __init__(self):
        self.welab_doris = WelabDoris()
        self.default_alarm_threshold = 50
        self.doris_tables = self.get_doris_tables()
        
        
        
    def get_doris_tables(self):
        doris_tables = []
        config_tables = config.DORIS_ALARM_TABLE.split()
        for config_table in config_tables:
            split_table = config_table.split(":")
            if len(split_table) != 2:
                logger.error(f"config table:{alarm_table} is invalid")
                continue
            doris_tables.append({
                "table": split_table[0],
                "log_type": split_table[1]
            })
        logger.info(f"doris service tables:{doris_tables}")
        return doris_tables
    
    
    def task_check_services_handle(self, db: Session, interval_min: int):
        # 计算时间范围
        # 实际查询是延后1分钟查询历史日志
        delay_min = 1
        now = datetime.datetime.now()
        cst_begin_time = (now - timedelta(minutes=interval_min+delay_min)).strftime("%Y-%m-%d %H:%M:%S")
        cst_end_time = (now - timedelta(minutes=delay_min)).strftime("%Y-%m-%d %H:%M:%S")
        
        # 获取所有配置的服务告警策略
        services_strategy = MonitorDorisService.get_all_service(db)
        services_strategy = {
            service_strategy["service_name"]: service_strategy
            for service_strategy in services_strategy
        }
        services_keys = services_strategy.keys()
        
        service_alarm = []
        
        # 获取每个服务的错误日志数量
        for doris_table in self.doris_tables:
            result, services_error_count = self.welab_doris.get_services_error_count(doris_table["table"], cst_begin_time, cst_end_time)
            if not result:
                logger.error("doris get services error count failed")
                return False
            
            logger.info(f"doris_table:{doris_table['table']} get services_error_count => {services_error_count}")
            for service_error_count in services_error_count:
                service_info = {
                    "service_name": service_error_count["fields_app"],
                    "log_type": doris_table["log_type"],
                    "error_count": service_error_count["error_count"],
                    "cst_begin_time": cst_begin_time,
                    "cst_end_time": cst_end_time,
                    "alarm_threshold": self.default_alarm_threshold,
                    "app_url": ""
                }
                logger.info(f"doris service:{service_info['service_name']} => {service_info['error_count']} begin")
                
                # 对服务单独配置告警策略
                if service_info["service_name"] in services_keys:
                    service_strategy = services_strategy[service_info["service_name"]]
                    service_info["alarm_threshold"] = service_strategy["alarm_threshold"]
                    service_info["log_type"] = service_strategy["log_type"]
                    
                    # 检查配置的告警时间范围
                    if not self.check_alarm_plans(service_strategy["alarm_plans"]):
                        logger.info(f"doris service:{service_info['service_name']} not in alarm plans")
                        continue
                    
                # 错误日志数量检查
                service_info["error_count"] = self.get_service_exclude_count(db, doris_table["table"], service_info)
                if service_info["error_count"] < service_info["alarm_threshold"]:
                    logger.info(f"doris service:{service_info['service_name']} < threshold")
                    continue
                
                # 随机获取100条错误日志
                exclude_message = self.get_service_exclude_message(db, doris_table["table"], service_info)
                # 根据错误日志，分析出最有特征的一条日志
                service_info["error_message"] = character_log_output(exclude_message)
                
                # 获取青鸾界面的url短连接
                service_info["app_url"] = MonitorDorisUrlHandler().get_service_short_url(db, doris_table["table"], service_info)
                
                service_alarm.append(service_info)
                logger.info(f"doris service:{service_info['service_name']} require alarm =>{service_info}")
            
        # 发送告警信息
        nc_log_alarm_handler = NCLogAlarmHandler()   
        result = nc_log_alarm_handler.post_log_alarm(db, "service", service_alarm) 
        logger.info(f"All doris service rules have been processed")
        return result
    
            
            
    def get_service_exclude_count(self, db: Session, doris_table: str, service_info: dict):
        # 获取服务配置的屏蔽关键字
        exclude_keywords = MonitorDorisExcludeHandler().get_exclude_keywords(db, service_info["service_name"])
        # 没有屏蔽关键字，则直接返回
        if len(exclude_keywords) == 0:
            return service_info["error_count"]
        
        # 排除屏蔽关键字后，获取错误日志的数量
        result, exclude_count = self.welab_doris.get_service_exclude_count(doris_table,
                                                       service_info["service_name"], 
                                                       service_info["cst_begin_time"],
                                                       service_info["cst_end_time"], 
                                                       exclude_keywords)
        if not result:
            logger.error("doris get service exclude count failed")
            return service_info["error_count"]
        
        result = exclude_count[0]["error_count"]
        logger.info(f"doris service:{service_info['service_name']} exclude error_count:{result}")
        return result
        

    def get_service_exclude_message(self, db: Session, doris_table: str, service_info: dict):
        # 获取服务配置的屏蔽关键字
        exclude_keywords = MonitorDorisExcludeHandler().get_exclude_keywords(db, service_info["service_name"])
        
        # 排除屏蔽关键字后的错误日志信息
        result, exclude_message = self.welab_doris.get_service_exclude_message(doris_table,
                                                       service_info["service_name"], 
                                                       service_info["cst_begin_time"],
                                                       service_info["cst_end_time"], 
                                                       exclude_keywords)
        if not result:
            logger.error("doris get service exclude message failed")
            return []
        
        result = [item["message"] for item in exclude_message]
        return result

            
    def check_alarm_plans(self, alarm_plans: list):
        now = datetime.datetime.now()
        now_time = now.time()
        for alarm_plan in alarm_plans:
            start_time = datetime.datetime.strptime(alarm_plan["start_time"], "%H:%M:%S").time()
            end_time = datetime.datetime.strptime(alarm_plan["end_time"], "%H:%M:%S").time()
            # 判断时间
            if now.weekday() in alarm_plan["date_range"] and now_time>start_time and now_time<end_time:
                return True
        
        return False
    
    

    def get_doris_service_metrics(self, db: Session):
        """
        将doris拉取日志的状态信息返回到prometheus
        """
        # 配置两个指标
        registry = CollectorRegistry(auto_describe=False)
        welab_doris_routine_load_delay = Gauge("welab_doris_routine_load_delay", "The delay time for Doris to pull logs, second",
                             ["name"], registry=registry)
        welab_doris_routine_load_state = Gauge("welab_doris_routine_load_state", "doris routine load state, 1:ok, 0:failed",
                             ["name"], registry=registry)
        welab_doris_routine_load_lag = Gauge("welab_doris_routine_load_lag", "doris routine load lag, 1:ok, 0:failed",
                             ["name", "partition"], registry=registry)
        
        # 统计每个表最新的日志时间距离现在过去的秒数
        now = datetime.datetime.now()
        tables = ["cdh_qingluan_log", "java_log", "openresty_access_log"]
        for table in tables:
            result, delay_time = self.welab_doris.get_service_last_time(table)
            if not result or len(delay_time)==0:
                logger.error(f"table:{table} get service last time faield")
                welab_doris_routine_load_delay.labels(name=table).set(10000)
            else:
                delay_seconds = int((now - delay_time[0]["logtime"]).total_seconds())
                welab_doris_routine_load_delay.labels(name=table).set(delay_seconds)
            
        result, routine_loads = self.welab_doris.get_routine_loads()
        if not result or len(routine_loads)==0:
            welab_doris_routine_load_state.labels(name="welab-doris").set(0)
        else:
            welab_doris_routine_load_state.labels(name="welab-doris").set(1)
            for routine_load in routine_loads:
                if routine_load["State"] != "RUNNING":
                    welab_doris_routine_load_state.labels(name=routine_load["Name"]).set(0)
                else:
                    welab_doris_routine_load_state.labels(name=routine_load["Name"]).set(1)
                
                lag_info = json.loads(routine_load["Lag"])
                for partition, lag_value in lag_info.items():
                    welab_doris_routine_load_lag.labels(name=routine_load["Name"], partition=partition).set(lag_value)
                
        
        return prometheus_client.generate_latest(registry)
    
    
    

    
def test():
    from common.database import SessionLocal
    session = SessionLocal()
    interval_min = 5
    monitor_doris_service_handler = MonitorDorisServiceHandler()
    result = monitor_doris_service_handler.get_doris_service_metrics(session)
    print(result)


if __name__ == '__main__':
    test()